iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
Software Development

輕鬆學習設計模式Design Pattern系列 第 8

Day 8 代理模式 Proxy Pattern

  • 分享至 

  • xImage
  •  

在日常生活中,我們常常遇到這樣的情況:你想要完成某件事情,但由於種種原因,你無法直接接觸到那個目標,於是你請來了一個「代理」來幫你處理一切。例如你需要買一台很難找的限量版遊戲機,但由於時間或距離問題,你沒辦法親自去購買,所以你委託了一個代購服務。而這個代購就是你的「代理」。在軟體設計中,代理模式 Proxy Pattern 就是利用類似的概念,讓一個物件(代理)代表另一個物件來控制對它的存取。聽起來有點抽象?別擔心,我們馬上進入正題。

什麼是代理模式?

代理模式是一種結構型設計模式,代理模式的核心思想是:為某個物件提供一個替代者,以控制對這個物件的存取。這樣做的好處是你可以在不改變原物件的情況下,新增一些額外的功能,或是對存取進行控制。代理模式可以被看作是一個中間層,這個中間層負責把客戶端的請求轉發給真實物件,並在過程中執行一些額外的邏輯。

假如你是一位繁忙的企業高階經理,每天都有無數的會議和決策要處理。這時候一個得力的助理就顯得重要。助理可以幫你過濾不重要的事物,安排你的行程,甚至代表你參加一些會議。這個助理就是現實生活中的「代理」。

簡單來說,代理模式中有三個角色:

  1. Subject:定義了客戶端可以呼叫的方法。
  2. RealSubject:實際執行請求的真實物件。
  3. Proxy:代理物件,負責控制對 RealSubject 的存取。

代理模式在網頁載入中的應用

讓我們以網頁載入的例子來解釋代理模式的實際應用。假設我們有一個網站,其中包含許多高解析度的圖片,這些圖片可能會因為其大容量而導致網頁載入緩慢。為了優化使用者體驗,我們可以使用代理模式,先顯示一個縮圖,等使用者真的需要查看大圖時,再去載入原圖。

首先我們定義一個 Image 介面,裡面有一個方法 display,用來顯示圖片,

// 抽象介面 Subject
class Image {
public:
    virtual void display() = 0;
    virtual ~Image() = default;
};

接下來我們實現這個介面來載入並顯示實際的圖片,

// 實際主題 RealSubject
class RealImage : public Image {
private:
    std::string filename;

    void loadFromDisk() {
        std::cout << "Loading " << filename << " from disk\n";
    }

public:
    RealImage(const std::string& filename) : filename(filename) {
        loadFromDisk();
    }

    void display() override {
        std::cout << "Displaying " << filename << std::endl;
    }
};

最後我們實現一個 ProxyImage 來控制對 RealImage 的存取,

// 代理物件 Proxy
class ProxyImage : public Image {
private:
    std::string filename;
    std::unique_ptr<RealImage> realImage;

public:
    ProxyImage(const std::string& filename) : filename(filename), realImage(nullptr) {}

    void display() override {
        if (!realImage) {
            // lazy initialization
            realImage = std::make_unique<RealImage>(filename);
        }
        realImage->display();
    }
};

在客戶端只需透過代理物件來存取圖片,而不必直接接觸到大圖片本身:

// 客戶端程式碼
int main() {
    std::unique_ptr<Image> image1 = std::make_unique<ProxyImage>("photo1.jpg");
    std::unique_ptr<Image> image2 = std::make_unique<ProxyImage>("photo2.jpg");

    // 圖像將從磁碟載入
    image1->display();
    
    // 圖像不會再次從磁碟載入,直接顯示
    image1->display();

    // 圖像將從磁碟載入
    image2->display();

    return 0;
}

在這個例子中,第一次呼叫 display() 方法時,代理會建立並載入真實圖片。當第二次呼叫時,圖片已經被載入過,因此不會再次載入,直接顯示圖片內容。這樣的處理方式避免了不必要的效能消耗,還提升了效率。

代理模式的優缺點

代理模式帶來了許多好處,最顯而易見的是它能讓我們有效地控制對目標物件的存取。這表示我們可以根據需求延遲載入資源、設定存取權限,甚至在某些情況下,讓一個代理物件負責管理多個請求,進而優化系統的效能。另外代理模式提供了一種優雅的方式,在不改變原有類別的基礎上新增額外功能,這讓我們的程式設計更加靈活,能更好地應對未來的變化。

代理模式也不是沒有缺點,導入代理會在客戶端和真實物件之間增加一個間接層,這可能會導致系統變得更加複雜。如果代理類別中包含了太多額外的邏輯,可能會影響效能。

總結

代理模式是一個實用且靈活的設計模式,適合用於需要控制存取或增加功能的場景中。它的應用非常廣泛,例如虛擬代理、保護代理、智慧代理等,每一種代理模式都有其獨特的價值。但我們也要注意避免因為濫用代理模式而導致系統過度複雜。

就像現實生活中的經紀人或助理一樣,代理為我們處理了許多複雜的細節,讓我們能夠專注於更重要的事務。下次當你面臨需要控制對對象的存取,或者需要在存取前後添加額外邏輯的情況時,不妨考慮使用代理模式。它可能正是你所需要的解決方案。

更多C++語言相關的文章,歡迎追蹤我的部落格。
https://shengyu7697.github.io/cpp-proxy-pattern/


上一篇
Day 7 工廠方法模式 Factory Method Pattern
下一篇
Day 9 命令模式 Command Pattern
系列文
輕鬆學習設計模式Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言